package com.michaelbulava.TFS.tpignore;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.michaelbulava.TFS.log.FileLogger;
import com.microsoft.tfs.util.Check;
import com.microsoft.tfs.util.NewlineUtils;

import java.io.*;
import java.nio.charset.Charset;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by mbulava on 6/23/2015.
 */
public class TPIgnoreDocument {
    public static final String DEFAULT_FILENAME = ".tpignore";

    private static final Logger log = FileLogger.getInstance(TPIgnoreDocument.class);

    private final List<Line> lines = new ArrayList<Line>();
    private final String newlineSequence;
    private final String charsetName;

    public TPIgnoreDocument(String newlineSequence, String charsetName){
        Check.notNull(newlineSequence, "newlineSequence");

        this.newlineSequence = newlineSequence;
        this.charsetName = charsetName;
    }



    public synchronized List<Line> getLines(){
        return new ArrayList<Line>(this.lines);
    }

    public synchronized void addLine(Line line){
        Check.notNull(line, "line");
        this.lines.add(line);
    }

    public synchronized void setLine(int index, Line line) {
        Check.notNull(line, "line");
        this.lines.set(index, line);
    }

    public synchronized void setLines(List<Line> lines) {
        this.lines.clear();
        this.lines.addAll(lines);
    }

    public synchronized Line getLine(int index) {
        return (Line)this.lines.get(index);
    }

    public synchronized String toString() {
        StringBuilder sb = new StringBuilder();
        for (Line line : this.lines) {
            sb.append(line.getContents());
            sb.append(this.newlineSequence);
        }
        return sb.toString();
    }

    public InputStream getInputStream() {
        String contents = toString();
        try {
            return new ByteArrayInputStream(this.charsetName != null ? contents.getBytes(this.charsetName) :
                    contents.getBytes(Charset.defaultCharset()));
        } catch (UnsupportedEncodingException e) {
            log.error(MessageFormat.format("Could not use original encoding {0} when preparing stream; using default instead",
                    new Object[]{this.charsetName}), e);
        }
        return new ByteArrayInputStream(contents.getBytes());
    }

    public synchronized void write(VirtualFile file, ProgressIndicator progressMonitor) {
        Check.notNull(file, "file");
        File myIOFile = new File(file.getPath());
        try{
            if (!myIOFile.exists())
                if (!myIOFile.createNewFile())
                    throw new IOException("failed to create ignore file.");
            FileOutputStream fos = new FileOutputStream(myIOFile, false);
            InputStream is = getInputStream();
            byte[] buffer = new byte[1024];
            int len = is.read(buffer);
            while (len > 0){
                fos.write(buffer, 0, len);
                len = is.read(buffer);
            }
            fos.flush();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static TPIgnoreDocument read(VirtualFile file)
            throws IOException
    {
        Check.notNull(file, "file");
        if (!file.exists())
            return new TPIgnoreDocument(NewlineUtils.PLATFORM_NEWLINE, null);
        String newline = null;
        Reader reader = null;

        try {
            reader = new InputStreamReader(file.getInputStream(), file.getCharset());
            newline = NewlineUtils.detectNewlineConvention(reader);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {}
                reader = null;
            }
        }
        if (newline == null) {
            newline = NewlineUtils.PLATFORM_NEWLINE;
        }
        TPIgnoreDocument ret = new TPIgnoreDocument(newline, file.getCharset().name());

        BufferedReader br = null;
        try {
            reader = new InputStreamReader(file.getInputStream(), file.getCharset());
            br = new BufferedReader(reader);
            String line;
            while ((line = br.readLine()) != null) {
                ret.addLine(new Line(line));
            }
            return ret;
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {}
            }
        }
    }


    public static void createDefaultTPIgnoreDocument(Module forModule){
        TPIgnoreCache cache = new TPIgnoreCache(forModule);
        VirtualFile ignoreFile = TPIgnoreCache.getIgnoreFile(forModule);

        if (ignoreFile == null){
            try {
                ignoreFile = LocalFileSystem.getInstance().createChildFile(forModule, forModule.getModuleFile().getParent(),
                        TPIgnoreDocument.DEFAULT_FILENAME);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            TPIgnoreDocument newDoc = new TPIgnoreDocument(NewlineUtils.PLATFORM_NEWLINE, "UTF-8");
            for(int i = 0; i < FILE_HEADER.length; i++){
                newDoc.addLine(new Line(FILE_HEADER[i]));
            }
            newDoc.write(ignoreFile, null);
        }
    }



    
    private static String[] FILE_HEADER = new String[] {
            "################################################################################",
            "# This .tpignore file was automatically created by Team Foundation Server - Version Control Integration.",
            "#",
            "# Local items matching filespecs in this file will not be added to version",
            "# control. This file can be checked in to share exclusions with others.",
            "#",
            "# Wildcard characters are * and ?. Patterns are matched recursively unless the",
            "# pattern is prefixed by the \\ character.",
            "#",
            "# You can prepend a path to a pattern to make it more specific. If you do,",
            "# wildcard characters are not permitted in the path portion.",
            "#",
            "# The # character at the beginning of a line indicates a comment.",
            "#",
            "# The ! prefix negates a pattern. This can be used to re-include an item after",
            "# it was excluded by a .tpignore file higher in the tree, or by the Team",
            "# Project Collection's global exclusions list.",
            "#",
            "# The / character is interpreted as a \\ character on Windows platforms.",
            "#",
            "# Examples:",
            "#",
            "#  # Excludes all files ending in .txt in Alpha\\Beta and all its subfolders.",
            "#  Alpha\\Beta\\*.txt",
            "#",
            "#  # Excludes all files ending in .cpp in this folder only.",
            "#  \\*.cpp",
            "#",
            "#  # Excludes all files ending in .cpp in this folder and all subfolders.",
            "#  *.cpp",
            "#",
            "#  # If \"Contoso\" is a folder, then Contoso and all its children are excluded.",
            "#  # If it is a file, then only the \"Contoso\" in this folder is excluded.",
            "#  \\Contoso",
            "#",
            "#  # If Help.exe is excluded by a higher .tpignore file or by the Team Project",
            "#  # Collection global exclusions list, then this pattern re-includes it in",
            "#  # this folder only.",
            "#  !\\Help.exe    ",
            "#",
            "#  # If a file is ignored in the Version Control > Ignored Files settings",
            "#  # of IntelliJ, it will always be ignored.  IntelliJ ignores always will take",
            "#  # priority over any settings in this file.",
            "#",

            "################################################################################",
            "/$tf/",
            "/out/",
            "/build/"
    };
}
